using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using DirkRettschlag.Utils;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace Departure
{
    public partial class ctrlStation : UserControl
    {
        #region Delegates
        
        private delegate void RequestDelegate(string Source, bool Error);
        private delegate void BuildDataDelegate(string Time, string Train, string Icon, string Direction, string Platform, string Later, string FullDateTime, string Error, bool isError, bool done, bool update, int currentitem, int allitems);
        public delegate void SetRefreshButtonEventDelegate(string internalName, bool enabled);
        public delegate void SetCountdownEventDelegate(string countdownTime, string train, string later);

        #endregion Delegates

        #region Events

        public event SetRefreshButtonEventDelegate OnSetRefreshButton;
        public event SetCountdownEventDelegate OnSetCountdown;

        #endregion Events

        #region Variables

        StationSettings stationSettings;
        GeneralSettings generalSettings;
        SkinSettings skinSettings;
        bool isUpdating;
        DataTable dt;
        bool vga;

        string status = string.Empty;

        public Webservice.WebserviceWrapper ws;

        // Background drawing
        Bitmap backBufferBitmap;
        Graphics backBuffer;

        #endregion Variables

        #region Properties

        public StationSettings StationSettings
        {
            get { return this.stationSettings; }
            set { this.stationSettings = value; }
        }

        public GeneralSettings GeneralSettings
        {
            set { this.generalSettings = value; }
        }

        public SkinSettings SkinSettings
        {
            set 
            {
                this.skinSettings = value;
                this.ForeColor = skinSettings.FontColor;
                this.kListControlStation.SkinSettings = skinSettings;
                Invalidate();
            }
        }

        public bool IsUpdating
        {
            get { return this.isUpdating; }
        }

        public bool VGA
        {
            get { return this.vga; }
            set { this.vga = value; }
        }

        public bool IsFocused
        {
            get { return kListControlStation.IsFocused; }
            set { kListControlStation.IsFocused = value; }
        }

        #endregion Properties
                
        #region Constructor
        public ctrlStation(string internalName, GeneralSettings generalSettings, Settings settings, bool vga)
        {
            InitializeComponent();

            stationSettings = new StationSettings(internalName, settings);

            stationSettings.getSettings();

            this.generalSettings = generalSettings;
            this.vga = vga;
            this.kListControlStation.VGA = vga;
            this.kListControlStation.HookEvents();

            ws = new Departure.Webservice.WebserviceWrapper();

            dt = new DataTable("Departure");

            dt.Columns.Add("Time", typeof(string));
            dt.Columns.Add("Train", typeof(string));
            dt.Columns.Add("Icon", typeof(string));
            dt.Columns.Add("Direction", typeof(string));
            dt.Columns.Add("Platform", typeof(string));
            dt.Columns.Add("Later", typeof(string));
            dt.Columns.Add("Full_DateTime", typeof(string));

            SetScaleDimensions();

            CreateBackBuffer();

        }
        #endregion Constructor

        #region Public Methods
                
        public void RefreshDepartures()
        {
            if (Utilities.Connected() || generalSettings.AutoConnect)
            {
                Cursor.Current = Cursors.WaitCursor;

                this.kListControlStation.Visible = false;
                this.kListControlStation.SuspendLayout();

                if (OnSetRefreshButton != null)
                    OnSetRefreshButton(this.Name, false);

                OnUpdateStatus("Retrieving Data...");
                lock (this.kListControlStation)
                {
                    this.kListControlStation.Clear();
                }

                isUpdating = true;

                GetData();
            }
        }

        public void RemovePastDepartures()
        {
            try
            {
                for (int i = this.kListControlStation.Count(0) - 1; i >= 0; i--)
                {
                    
                        double correction = 0;
                        string later = (this.kListControlStation[i] as ctrlStationItem).Later;
                        if (later != "" && later.IndexOf("approx. ") >= 0)
                            correction = double.Parse(later.Replace("approx. ", "").Replace(" minutes later", ""));

                        DateTime corrected = DateTime.FromFileTimeUtc(long.Parse((this.kListControlStation[i] as ctrlStationItem).FullDateTime)).AddMinutes(correction);
                        if (corrected < DateTime.Now)
                        {
                            this.kListControlStation.RemoveItem(this.kListControlStation[i]);
                        }
                    
                }
            }
            catch
            {
            }

        }

        public void SaveDepartures()
        {
            try
            {
                dt.Rows.Clear();
                for (int i = 0; i < this.kListControlStation.Count(0); i++)
                {
                    DataRow dr = dt.NewRow();
                    dr["Time"] = (this.kListControlStation[i] as ctrlStationItem).Time;
                    dr["Train"] = (this.kListControlStation[i] as ctrlStationItem).Train;
                    dr["Icon"] = (this.kListControlStation[i] as ctrlStationItem).Icon;
                    dr["Direction"] = (this.kListControlStation[i] as ctrlStationItem).Direction;
                    dr["Platform"] = (this.kListControlStation[i] as ctrlStationItem).Platform;
                    dr["Later"] = (this.kListControlStation[i] as ctrlStationItem).Later;
                    dr["Full_DateTime"] = (this.kListControlStation[i] as ctrlStationItem).FullDateTime;

                    dt.Rows.Add(dr);
                }
                dt.WriteXml(Utilities.AppPath() + "\\stations\\" + this.Name + ".xml");
            }
            catch
            {
            }

        }

        public void LoadDepartures()
        {
            if (!backgroundWorkerLoad.IsBusy)
                backgroundWorkerLoad.RunWorkerAsync();
        }

        public void SetScaleDimensions()
        {
            SizeF currentScreen = this.CurrentAutoScaleDimensions;

            this.kListControlStation.TrainIcons = new SortedList<string, Bitmap>();

            if (vga)
            {
                if (this.stationSettings.UseShowPlatform)
                    this.kListControlStation.ItemHeight = 92;
                else
                    this.kListControlStation.ItemHeight = 64;

                this.kListControlStation.FastAccessWidth = 40;

                this.kListControlStation.TrainIcons.Add("ice", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ice_48x48"));
                this.kListControlStation.TrainIcons.Add("ec_ic", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ec_ic_48x48"));
                this.kListControlStation.TrainIcons.Add("ir", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ir_48x48"));
                this.kListControlStation.TrainIcons.Add("re", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("re_48x48"));
                this.kListControlStation.TrainIcons.Add("sbahn", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("sbahn_48x48"));
                this.kListControlStation.TrainIcons.Add("bus", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("bus_48x48"));
                this.kListControlStation.TrainIcons.Add("faehre", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("faehre_48x48"));
                this.kListControlStation.TrainIcons.Add("ubahn", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ubahn_48x48"));
                this.kListControlStation.TrainIcons.Add("tram", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("tram_48x48")); 
            }
            else
            {
                if (this.stationSettings.UseShowPlatform)
                    this.kListControlStation.ItemHeight = 46;
                else
                    this.kListControlStation.ItemHeight = 32;

                this.kListControlStation.FastAccessWidth = 20;

                this.kListControlStation.TrainIcons.Add("ice", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ice_24x24"));
                this.kListControlStation.TrainIcons.Add("ec_ic", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ec_ic_24x24"));
                this.kListControlStation.TrainIcons.Add("ir", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ir_24x24"));
                this.kListControlStation.TrainIcons.Add("re", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("re_24x24"));
                this.kListControlStation.TrainIcons.Add("sbahn", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("sbahn_24x24"));
                this.kListControlStation.TrainIcons.Add("bus", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("bus_24x24"));
                this.kListControlStation.TrainIcons.Add("faehre", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("faehre_24x24"));
                this.kListControlStation.TrainIcons.Add("ubahn", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("ubahn_24x24"));
                this.kListControlStation.TrainIcons.Add("tram", (Bitmap)Departure.Properties.Resources.ResourceManager.GetObject("tram_24x24")); 
            }


            
        }

        delegate void OnUpdateStatusCallback(string status);
        public void OnUpdateStatus(string status)
        {
            if (this.InvokeRequired)
            {
                OnUpdateStatusCallback d = new OnUpdateStatusCallback(OnUpdateStatus);
                this.Invoke(d, new object[] { status });
            }
            else
            {
                this.status = status;
                Invalidate();
            }
        }

        public void HookKeyDown(Control control)
        {
            control.KeyDown += new KeyEventHandler(OnKeyDown);
        }

        #endregion Public Methods

        #region Private Methods

        private void LoadDeparturesThread()
        {
            isUpdating = true;
            Invoking.SetCursor(this, Cursors.WaitCursor);
            Invoking.SetControlVisible(this.kListControlStation, false);
            Invoking.SetControlLayoutMode(this.kListControlStation, true);

            if (System.IO.File.Exists(Utilities.AppPath() + "\\stations\\" + this.Name + ".xml"))
            {
                dt.Rows.Clear();
                dt.ReadXml(Utilities.AppPath() + "\\stations\\" + this.Name + ".xml");

                foreach (DataRow dr in dt.Rows)
                {
                    AddStationItem(dr["Time"].ToString(), dr["Train"].ToString(), dr["Icon"].ToString(), dr["Direction"].ToString(), dr["Platform"].ToString(), dr["Later"].ToString(), dr["Full_DateTime"].ToString());
                }
            }
            Invoking.SetControlLayoutMode(this.kListControlStation, false);
            Invoking.SetControlVisible(this.kListControlStation, true);
            Invoking.SetCursor(this, Cursors.Default);
            
            isUpdating = false;
            
            if (OnSetRefreshButton != null)
                OnSetRefreshButton(this.Name, true);
        }
        
        private void GetData()
        {

            if (generalSettings.UseWebService)
            {
                Thread t = new Thread(new ThreadStart(GetDeparturesThread));
                t.IsBackground = true; //make them a daemon - prevent thread callback issues
                t.Start();
            }
            else
            {
                string URL = "";
                if (stationSettings.StationID == "")
                    URL = "http://mobile.bahn.de/bin/mobil/bhftafel.exe/en?input=" + stationSettings.Station + "&time=actual&boardType=dep" + stationSettings.Transfers + "&start=Suchen&ao=yes";
                else
                    URL = "http://mobile.bahn.de/bin/mobil/bhftafel.exe/en?input=" + stationSettings.StationID + "&time=actual&boardType=dep" + stationSettings.Transfers + "&start=Suchen&ao=yes";

                RequestDelegate requestDone = new RequestDelegate(RequestCallback);

                RequestThread wt = new RequestThread(this, requestDone, URL, generalSettings.UserAgent, generalSettings.HTTPHeader);
                Thread t = new Thread(new ThreadStart(wt.RunProcess));
                t.IsBackground = true; //make them a daemon - prevent thread callback issues
                t.Start();
            }

        }

        private void GetDeparturesThread()
        {
            Departure.Webservice.Methods.Input input = new Departure.Webservice.Methods.Input();

            input.Station = stationSettings.Station;
            input.StationID = stationSettings.StationID;

            input.Transfers = stationSettings.Transfers;

            input.TransfersSelected = stationSettings.TransfersSelected;

            input.UseTrainsFilter = stationSettings.UseTrainsFilter;
            input.TrainsFilter = stationSettings.TrainsFilter;

            input.UseDirectionsFilter = stationSettings.UseDirectionsFilter;
            input.DirectionsFilter = stationSettings.DirectionsFilter;

            input.UsePlatformsFilter = stationSettings.UsePlatformsFilter;
            input.PlatformsFilter = stationSettings.PlatformsFilter;

            ws.setURL(generalSettings.WebServiceURL);

            try
            {

                OnUpdateStatus("Retrieving Departures...");

                string xmlDepartures = ws.GetDepartures(generalSettings.DeviceID, input);
                
                StringReader xmlIn = new StringReader(xmlDepartures);

                dt.Rows.Clear();
                dt.ReadXml(xmlIn);

                int i = 0;

                foreach (DataRow dr in dt.Rows)
                {
                    OnUpdateStatus(string.Format("Retrieving Departures ({0}/{1})", i++, dt.Rows.Count));
                    AddStationItem(dr["Time"].ToString(), dr["Train"].ToString(), dr["Icon"].ToString(), dr["Direction"].ToString(), dr["Platform"].ToString(), dr["Later"].ToString(), dr["Full_DateTime"].ToString());
                }
            }
            catch (Exception ex)
            {
                Invoking.SetCursor(this, Cursors.Default);

                MessageBox.Show(ex.Message, "An error occured");

            }

            OnUpdateStatus("");
            Invoking.SetControlLayoutMode(this.kListControlStation, false);
            Invoking.SetControlVisible(this.kListControlStation, true);
            isUpdating = false;

            Invoking.SetCursor(this, Cursors.Default);

            if (OnSetRefreshButton != null)
                OnSetRefreshButton(this.Name, true);
        }
              
        private void RequestCallback(string Source, bool Error)
        {

            if (Error == false)
            {
                BuildData(Source);

            }
            else
            {
                OnUpdateStatus("");
                isUpdating = false;
                this.kListControlStation.ResumeLayout(true);
                this.kListControlStation.Visible = true;
                Cursor.Current = Cursors.Default;
                MessageBox.Show(Source, "An error occured");
            }
        }

        private void BuildData(string source)
        {
           
            OnUpdateStatus("Filtering Data...");

            BuildDataDelegate buildDataDone = new BuildDataDelegate(BuildDataCallback);

            BuildDataThread wt = new BuildDataThread(this, buildDataDone, source, stationSettings);
            Thread t = new Thread(new ThreadStart(wt.RunProcess));
            t.IsBackground = true; //make them a daemon - prevent thread callback issues
            t.Start();

        }

        private void BuildDataCallback(string Time, string Train, string Icon, string Direction, string Platform, string Later, string FullDateTime, string Error, bool isError, bool done, bool update, int currentitem, int allitems)
        {
            if (isError)
            {
                OnUpdateStatus("");
                isUpdating = false;
                this.kListControlStation.ResumeLayout(true);
                this.kListControlStation.Visible = true;
                Cursor.Current = Cursors.Default;
                MessageBox.Show(Error, "An error occured");
            }
            else if (done)
            {
               
                OnUpdateStatus("");
                isUpdating = false;
                this.kListControlStation.ResumeLayout(true);
                this.kListControlStation.Visible = true;
                Cursor.Current = Cursors.Default;

                if (OnSetRefreshButton != null)
                    OnSetRefreshButton(this.Name, true);

            }
            else if (update)
            {
                OnUpdateStatus(string.Format("Filtering Trains ({0}/{1})", currentitem, allitems));
            }
            else
            {
                AddStationItem(Time, Train, Icon, Direction, Platform, Later, FullDateTime);
            }

        }

        private void AddStationItem(string Time, string Train, string Icon, string Direction, string Platform, string Later, string FullDateTime)
        {
            AddStationItem(this.kListControlStation, Time, Train, Icon, Direction, Platform, Later, FullDateTime);
        }

        delegate void AddStationItemCallback(Control co, string Time, string Train, string Icon, string Direction, string Platform, string Later, string FullDateTime);
        public void AddStationItem(Control co, string Time, string Train, string Icon, string Direction, string Platform, string Later, string FullDateTime)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (co.InvokeRequired)
            {
                AddStationItemCallback d = new AddStationItemCallback(AddStationItem);
                co.Invoke(d, new object[] { co, Time, Train, Icon, Direction, Platform, Later, FullDateTime });
            }
            else
            {
                ctrlStationItem stationItem = new ctrlStationItem(Time, Train, Icon, Direction, Platform, Later, FullDateTime, vga);

                stationItem.XIndex = 0;
                stationItem.YIndex = kListControlStation.Count(0);
                stationItem.FastAccessValue = stationItem.Time;

                kListControlStation.AddItem(stationItem);

                stationItem.SetFontStyle();

            }
        }

        private void kListControlStation_OnItemDoubleClicked(object sender, EventArgs e)
        {
            string trainDisplay = "";

            ctrlStationItem item = (ctrlStationItem)kListControlStation.SelectedItem;

            if (item != null)
            {
                trainDisplay = generalSettings.CountdownTrainFormat.Replace("%TI%", item.Time).Replace("%TR%", item.Train).Replace("%DI%", item.Direction).Replace("%PL%", item.Platform);

                if (OnSetCountdown != null)
                    OnSetCountdown(item.FullDateTime, trainDisplay, item.Later);
            }
        }
        
        private void backgroundWorkerLoad_DoWork(object sender, DoWorkEventArgs e)
        {
            LoadDeparturesThread();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            this.kListControlStation.ItemWidth = this.ClientSize.Width;
            CreateBackBuffer();
            Invalidate();
        }



        /// <summary>
        /// Paints the background of the control.
        /// </summary>
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // Do nothing
        }

        /// <summary>
        /// Paints the control.
        /// </summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (backBuffer != null)
            {
                backBuffer.DrawImage(skinSettings.BackGround, this.Bounds, e.ClipRectangle, GraphicsUnit.Pixel);

                if (vga)
                    backBuffer.DrawString(status, this.Font, new SolidBrush(skinSettings.FontColor), 1, this.Bounds.Height - 32);
                else
                    backBuffer.DrawString(status, this.Font, new SolidBrush(skinSettings.FontColor), 1, this.Bounds.Height - 16);

                e.Graphics.DrawImage(backBufferBitmap, 0, 0);
            }
            else
            {
                base.OnPaint(e);
            }
        }

        /// <summary>
        /// Cleans up the background paint buffer.
        /// </summary>
        private void CleanupBackBuffer()
        {
            if (backBufferBitmap != null)
            {
                backBufferBitmap.Dispose();
                backBufferBitmap = null;
                backBuffer.Dispose();
                backBuffer = null;
            }
        }

        /// <summary>
        /// Creates the background paint buffer.
        /// </summary>
        private void CreateBackBuffer()
        {
            CleanupBackBuffer();

            backBufferBitmap = new Bitmap(Bounds.Width, Bounds.Height);
            backBuffer = Graphics.FromImage(backBufferBitmap);
        }

        //protected override void OnKeyDown(KeyEventArgs e)
        //{
        //    OnKeyDown(null, e);
        //}

        protected void OnKeyDown(object sender, KeyEventArgs e)
        {
            base.OnKeyDown(e);
        }

        #endregion Private Methods
    }
}
